﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DZLib.ExtendFramework.Generic
{
    public static class ExtendedIEnumrable
    {
        public static IEnumerable<T> FullCopy<T>(this IEnumerable<T> src)
        {
            if (src == null)
            {
                return null;
            }
            else
            {
                var copiedList = new List<T>();
                foreach (var item in src)
                {
                    copiedList.Add(item);
                }
                return copiedList;
            }
        }

        private static bool IsInRange(int start, int end, int index)
        {
            if (end < start)
            {
                throw new ArgumentOutOfRangeException("Start should be less or equal than end.");
            }
            else
            {
                if (index >= start && index <= end)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        public static bool IndexIsInRange<T>(this IEnumerable<T> src, int index)
        {
            return IsInRange(start: 0, end: src.Count() - 1, index: index);
        }

        public static IEnumerable<T> Sub<T>(this IEnumerable<T> src, int startIndex)
        {
            if (src == null)
            {
                return null;
            }
            else if (src.Count() > 0)
            {
                if (IsInRange(start: 0, end: src.Count() - 1, index: startIndex))
                {
                    var newList = new List<T>();
                    for (int i = startIndex; i < src.Count(); i++)
                    {
                        newList.Add(src.ElementAt(i));
                    }
                    return newList;
                }
                else
                {
                    throw new IndexOutOfRangeException();
                }
            }
            else
            {
                return src;
            }
        }

        public static IEnumerable<T> Sub<T>(this IEnumerable<T> src, int startIndex, int length)
        {
            if (src == null)
            {
                return null;
            }
            else if (src.Count() > 0)
            {
                if (IsInRange(start: 0, end: src.Count() - 1, index: startIndex))
                {
                    var newList = new List<T>();
                    if (length == 0)
                    {
                        return newList;
                    }
                    if (IsInRange(start: startIndex, end: src.Count() - 1, index: startIndex + length - 1))
                    {
                        for (int i = startIndex; i < startIndex + length; i++)
                        {
                            newList.Add(src.ElementAt(i));
                        }
                        return newList;
                    }
                }
                throw new IndexOutOfRangeException();
            }
            else
            {
                return src;
            }
        }

        public static IEnumerable<IEnumerable<T>> SetSplit<T>(this IEnumerable<T> src, IEnumerable<T> spliteSet)
        {
            if (spliteSet.Count() == 0)
            {
                throw new ArgumentException("Splite Set should not be empty.");
            }
            else
            {
                var result = new List<List<T>>();
                var current = new List<T>();
                var flag = false;
                foreach (var item in src)
                {
                    if (spliteSet.Contains(item))
                    {
                        result.Add(current);
                        current = new List<T>();
                        flag = true;
                    }
                    else
                    {
                        current.Add(item);
                        flag = false;
                    }
                }
                if (current.Count != 0 && !flag)
                {
                    result.Add(current);
                }
                if (current.Count == 0 && result.Count == 0)
                {
                    result.Add(current);
                }
                return result;
            }
        }

        public static int IndexOf<T>(this IEnumerable<T> src, T target)
        {
            for (int i = 0; i < src.Count(); i++)
            {
                if (src.ElementAt(i).Equals(target))
                {
                    return i;
                }
            }
            return -1;
        }

        public static int IndexOf<T>(this IEnumerable<T> src, T target, int offset)
        {
            if (src.Count() == 0)
            {
                return -1;
            }
            else if (IsInRange(0, src.Count() - 1, offset))
            {
                for (int i = offset; i < src.Count(); i++)
                {
                    if (src.ElementAt(i).Equals(target))
                    {
                        return i;
                    }
                }
                return -1;
            }
            throw new IndexOutOfRangeException();
        }

        public static int IndexOf<T>(this IEnumerable<T> src, IEnumerable<T> targetSet)
        {
            if (targetSet.Count() != 0)
            {
                int position = -1;
                bool flag = false;
                int offset = 0;
                for (int i = 0; i < src.Count(); i++)
                {
                    if (offset < targetSet.Count())
                    {
                        if (src.ElementAt(i).Equals(targetSet.ElementAt(offset)))
                        {
                            offset++;
                            if (!flag)
                            {
                                if (src.Count() - position < targetSet.Count())
                                {
                                    return -1;
                                }
                                else
                                {
                                    position = i;
                                    flag = true;
                                }
                            }
                        }
                        else
                        {
                            position = -1;
                            flag = false;
                            offset = 0;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                return position;
            }
            else
            {
                throw new ArgumentException("Target set should not be empty");
            }
        }

        public static IEnumerable<T> Clip<T>(this IEnumerable<T> src, IEnumerable<T> targetSet)
        {
            if (src.Count() == 0 || targetSet.Count() == 0)
            {
                return new List<T>();
            }
            else if (src.Count() <= targetSet.Count())
            {
                return new List<T>(src.ToArray());
            }
            else
            {
                return src.Sub(0, targetSet.Count());
            }
        }

        public static IEnumerable<T> Expand<T>(this IEnumerable<T> src, int count)
        {
            return src.Expand(count, default(T));
        }

        public static IEnumerable<T> Expand<T>(this IEnumerable<T> src, int count, T initValue)
        {
            if (src.Count() < count)
            {
                var result = src.ToList();
                var length = count - src.Count();
                for (int i = 0; i < length; i++)
                {
                    result.Add(initValue);
                }
                return result;
            }
            else
            {
                return src;
            }
        }

        public static IEnumerable<T> Resize<T>(this IEnumerable<T> src, int count)
        {
            return src.Resize(count, default(T));
        }

        public static IEnumerable<T> Resize<T>(this IEnumerable<T> src, int count, T initValue)
        {
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (src.Count() < count)
            {
                return src.Expand(count, initValue);
            }
            else if (src.Count() > count)
            {
                return src.Sub(0, count);
            }
            else
            {
                return new List<T>(src.ToArray());
            }
        }

        public static int IndexOfMin<T>(this IEnumerable<T> src)
        {
            var min = src.Min();
            return src.IndexOf(min);
        }

        public static int IndexOfMax<T>(this IEnumerable<T> src)
        {
            var max = src.Max();
            return src.IndexOf(max);
        }
    }
}
